
#include <stddef.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include "setlocale.h"
#include "time_local.h"

typedef struct __tzrule_struct {
    char ch;
    int m;
    int n;
    int d;
    int s;
    time_t change;
    long offset; /* Match type of _timezone. */
} __tzrule_type;

typedef struct __tzinfo_struct {
    int __tznorth;
    int __tzyear;
    __tzrule_type __tzrule[2];
} __tzinfo_type;

__tzinfo_type *__gettzinfo(void);

/* Shared timezone information for libc/time functions.  */
static __tzinfo_type tzinfo = {1, 0,
                               {{'J', 0, 0, 0, 0, (time_t) 0, 0L},
                                {'J', 0, 0, 0, 0, (time_t) 0, 0L}
                               }
};

__tzinfo_type *
__gettzinfo(void) {
    return &tzinfo;
}

#define CHECK_LENGTH()    if (len < 0 || (count += len) >= maxsize) \
              return 0
#  define CHAR        char        /* string type basis */
#  define CQ(a)        a        /* character constant qualifier */
#  define SFLG                /* %s flag (null for normal char) */
#  define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x), ctloc)
//#  define snprintf	sniprintf	/* avoid to pull in FP functions. */
#  define TOLOWER(c)    tolower((int)(unsigned char)(c))
#  define STRTOUL(c, p, b) strtoul((c),(p),(b))
#  define STRCPY(a, b)    strcpy((a),(b))
#  define STRCHR(a, b)    strchr((a),(b))
#  define STRLEN(a)    strlen(a)

/* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
   -1, 0, or 1 as the adjustment to add to the year for the ISO week
   numbering used in "%g%G%V", avoiding overflow.  */
static int
iso_year_adjust(const struct tm *tim_p) {
    /* Account for fact that tm_year==0 is year 1900.  */
    int leap = isleap (tim_p->tm_year + (YEAR_BASE
                                         - (tim_p->tm_year < 0 ? 0 : 2000)));

    /* Pack the yday, wday, and leap year into a single int since there are so
       many disparate cases.  */
#define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
    switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap)) {
        case PACK (0, 5, 0): /* Jan 1 is Fri, not leap.  */
        case PACK (0, 6, 0): /* Jan 1 is Sat, not leap.  */
        case PACK (0, 0, 0): /* Jan 1 is Sun, not leap.  */
        case PACK (0, 5, 1): /* Jan 1 is Fri, leap year.  */
        case PACK (0, 6, 1): /* Jan 1 is Sat, leap year.  */
        case PACK (0, 0, 1): /* Jan 1 is Sun, leap year.  */
        case PACK (1, 6, 0): /* Jan 2 is Sat, not leap.  */
        case PACK (1, 0, 0): /* Jan 2 is Sun, not leap.  */
        case PACK (1, 6, 1): /* Jan 2 is Sat, leap year.  */
        case PACK (1, 0, 1): /* Jan 2 is Sun, leap year.  */
        case PACK (2, 0, 0): /* Jan 3 is Sun, not leap.  */
        case PACK (2, 0, 1): /* Jan 3 is Sun, leap year.  */
            return -1; /* Belongs to last week of previous year.  */
        case PACK (362, 1, 0): /* Dec 29 is Mon, not leap.  */
        case PACK (363, 1, 1): /* Dec 29 is Mon, leap year.  */
        case PACK (363, 1, 0): /* Dec 30 is Mon, not leap.  */
        case PACK (363, 2, 0): /* Dec 30 is Tue, not leap.  */
        case PACK (364, 1, 1): /* Dec 30 is Mon, leap year.  */
        case PACK (364, 2, 1): /* Dec 30 is Tue, leap year.  */
        case PACK (364, 1, 0): /* Dec 31 is Mon, not leap.  */
        case PACK (364, 2, 0): /* Dec 31 is Tue, not leap.  */
        case PACK (364, 3, 0): /* Dec 31 is Wed, not leap.  */
        case PACK (365, 1, 1): /* Dec 31 is Mon, leap year.  */
        case PACK (365, 2, 1): /* Dec 31 is Tue, leap year.  */
        case PACK (365, 3, 1): /* Dec 31 is Wed, leap year.  */
            return 1; /* Belongs to first week of next year.  */
    }
    return 0; /* Belongs to specified year.  */
#undef PACK
}


#ifdef _WANT_C99_TIME_FORMATS
static size_t
__strftime (CHAR *s, size_t maxsize, const CHAR *format,
            const struct tm *tim_p, struct __locale_t *locale,
            era_info_t **era_info, alt_digits_t **alt_digits)
#else /* !_WANT_C99_TIME_FORMATS */

static size_t
__strftime(CHAR *s, size_t maxsize, const CHAR *format,
           const struct tm *tim_p, struct __locale_t *locale)

#define __strftime(s, m, f, t, l, e, a)    __strftime((s),(m),(f),(t),(l))
#endif /* !_WANT_C99_TIME_FORMATS */

{
    size_t count = 0;
    int len = 0;
    const CHAR *ctloc;
#if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
    CHAR ctlocbuf[CTLOCBUFLEN];
#endif
    size_t i, ctloclen;
    CHAR alt;
    CHAR pad;
    unsigned long width;
    int tzset_called = 0;

    const struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale();
    for (;;) {
        while (*format && *format != CQ('%')) {
            if (count < maxsize - 1)
                s[count++] = *format++;
            else
                return 0;
        }
        if (*format == CQ('\0'))
            break;
        format++;
        pad = '\0';
        width = 0;

        /* POSIX-1.2008 feature: '0' and '+' modifiers require 0-padding with
           slightly different semantics. */
        if (*format == CQ('0') || *format == CQ('+'))
            pad = *format++;

        /* POSIX-1.2008 feature: A minimum field width can be specified. */
        if (*format >= CQ('1') && *format <= CQ('9')) {
            CHAR *fp;
            width = STRTOUL (format, &fp, 10);
            format = fp;
        }

        alt = CQ('\0');
        if (*format == CQ('E')) {
            alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS
#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
            if (!*era_info && *_CurrentTimeLocale->wera)
              *era_info = get_era_info (tim_p, _CurrentTimeLocale->wera);
#else
            if (!*era_info && *_CurrentTimeLocale->era)
              *era_info = get_era_info (tim_p, _CurrentTimeLocale->era);
#endif
#endif /* _WANT_C99_TIME_FORMATS */
        } else if (*format == CQ('O')) {
            alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS
#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
            if (!*alt_digits && *_CurrentTimeLocale->walt_digits)
              *alt_digits = get_alt_digits (_CurrentTimeLocale->walt_digits);
#else
            if (!*alt_digits && *_CurrentTimeLocale->alt_digits)
              *alt_digits = get_alt_digits (_CurrentTimeLocale->alt_digits);
#endif
#endif /* _WANT_C99_TIME_FORMATS */
        }

        switch (*format) {
            case CQ('a'):
                _ctloc (wday[tim_p->tm_wday]);
                for (i = 0; i < ctloclen; i++) {
                    if (count < maxsize - 1)
                        s[count++] = ctloc[i];
                    else
                        return 0;
                }
                break;
            case CQ('A'):
                _ctloc (weekday[tim_p->tm_wday]);
                for (i = 0; i < ctloclen; i++) {
                    if (count < maxsize - 1)
                        s[count++] = ctloc[i];
                    else
                        return 0;
                }
                break;
            case CQ('b'):
            case CQ('h'):
                _ctloc (mon[tim_p->tm_mon]);
                for (i = 0; i < ctloclen; i++) {
                    if (count < maxsize - 1)
                        s[count++] = ctloc[i];
                    else
                        return 0;
                }
                break;
            case CQ('B'):
                _ctloc (month[tim_p->tm_mon]);
                for (i = 0; i < ctloclen; i++) {
                    if (count < maxsize - 1)
                        s[count++] = ctloc[i];
                    else
                        return 0;
                }
                break;
            case CQ('c'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_t_fmt)
        _ctloc (era_d_t_fmt);
      else
#endif /* _WANT_C99_TIME_FORMATS */
                _ctloc (c_fmt);
                goto recurse;
            case CQ('r'):
                _ctloc (ampm_fmt);
                goto recurse;
            case CQ('x'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_fmt)
        _ctloc (era_d_fmt);
      else
#endif /* _WANT_C99_TIME_FORMATS */
                _ctloc (x_fmt);
                goto recurse;
            case CQ('X'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_t_fmt)
        _ctloc (era_t_fmt);
      else
#endif /* _WANT_C99_TIME_FORMATS */
                _ctloc (X_fmt);
            recurse:
                if (*ctloc) {
                    /* Recurse to avoid need to replicate %Y formation. */
                    len = __strftime(&s[count], maxsize - count, ctloc, tim_p,
                                     locale, era_info, alt_digits);
                    if (len > 0)
                        count += len;
                    else
                        return 0;
                }
                break;
            case CQ('C'): {
                /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
                   with 32-bit int.
                   %Y		%C		%y
                   2147485547	21474855	47
                   10000		100		00
                   9999		99		99
                   0999		09		99
                   0099		00		99
                   0001		00		01
                   0000		00		00
                   -001		-0		01
                   -099		-0		99
                   -999		-9		99
                   -1000		-10		00
                   -10000		-100		00
                   -2147481748	-21474817	48

                   Be careful of both overflow and sign adjustment due to the
                   asymmetric range of years.
                */
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info)
          len = snprintf (&s[count], maxsize - count, CQ("%" SFLG "s"),
                  (*era_info)->era_C);
        else
#endif /* _WANT_C99_TIME_FORMATS */
                {
                    CHAR fmtbuf[32];
                    char *pos = "";
                    int neg = tim_p->tm_year < -YEAR_BASE;
                    int century = tim_p->tm_year >= 0
                                  ? tim_p->tm_year / 100 + YEAR_BASE / 100
                                  : abs(tim_p->tm_year + YEAR_BASE) / 100;
                    if (width < 2)
                        width = 2;
                    if (neg)
                        width -= neg;
                    sprintf(fmtbuf, "%%s%%0%dd", width);
                    if (pad) /* '0' or '+' */
                    {
                        if (century >= 100 && pad == CQ('+'))
                            pos = "+";
                    }
                    len = snprintf(&s[count], maxsize - count, fmtbuf,
                                   neg ? "-" : pos, century);
                }
                CHECK_LENGTH ();
            }
                break;
            case CQ('d'):
            case CQ('e'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == CQ('O') && *alt_digits)
        {
          if (tim_p->tm_mday < 10)
              {
          if (*format == CQ('d'))
            {
              if (maxsize - count < 2) return 0;
              len = conv_to_alt_digits (&s[count], maxsize - count,
                        0, *alt_digits);
              CHECK_LENGTH ();
            }
          if (*format == CQ('e') || len == 0)
            s[count++] = CQ(' ');
        }
          len = conv_to_alt_digits (&s[count], maxsize - count,
                    tim_p->tm_mday, *alt_digits);
          CHECK_LENGTH ();
          if (len > 0)
        break;
        }
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count,
                               *format == CQ('d') ? CQ("%02d") : CQ("%2d"),
                               tim_p->tm_mday);
                CHECK_LENGTH ();
                break;
            case CQ('D'):
                /* %m/%d/%y */
                len = snprintf(&s[count], maxsize - count,
                               CQ("%02d/%02d/%02d"),
                               tim_p->tm_mon + 1, tim_p->tm_mday,
                               tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                                                   : abs(tim_p->tm_year + YEAR_BASE) % 100);
                CHECK_LENGTH ();
                break;
            case CQ('F'): { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change
	       that.  Recurse to avoid need to replicate %Y formation. */
                CHAR fmtbuf[32], *fmt = fmtbuf;

                *fmt++ = CQ('%');
                if (pad) /* '0' or '+' */
                    *fmt++ = pad;
                else
                    *fmt++ = '+';
                if (!pad)
                    width = 10;
                if (width < 6)
                    width = 6;
                width -= 6;
                if (width) {
                    len = snprintf(fmt, fmtbuf + 32 - fmt, CQ("%lu"), width);
                    if (len > 0)
                        fmt += len;
                }
                STRCPY (fmt, CQ("Y-%m-%d"));
                len = __strftime(&s[count], maxsize - count, fmtbuf, tim_p,
                                 locale, era_info, alt_digits);
                if (len > 0)
                    count += len;
                else
                    return 0;
            }
                break;
            case CQ('g'):
                /* Be careful of both overflow and negative years, thanks to
                   the asymmetric range of years.  */
            {
                int adjust = iso_year_adjust(tim_p);
                int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                                               : abs(tim_p->tm_year + YEAR_BASE) % 100;
                if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
                    adjust = 1;
                else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
                    adjust = -1;
                len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                               ((year + adjust) % 100 + 100) % 100);
                CHECK_LENGTH ();
            }
                break;
            case CQ('G'): {
                /* See the comments for 'C' and 'Y'; this is a variable length
                   field.  Although there is no requirement for a minimum number
                   of digits, we use 4 for consistency with 'Y'.  */
                int sign = tim_p->tm_year < -YEAR_BASE;
                int adjust = iso_year_adjust(tim_p);
                int century = tim_p->tm_year >= 0
                              ? tim_p->tm_year / 100 + YEAR_BASE / 100
                              : abs(tim_p->tm_year + YEAR_BASE) / 100;
                int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                                               : abs(tim_p->tm_year + YEAR_BASE) % 100;
                if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
                    sign = adjust = 1;
                else if (adjust > 0 && sign)
                    adjust = -1;
                year += adjust;
                if (year == -1) {
                    year = 99;
                    --century;
                } else if (year == 100) {
                    year = 0;
                    ++century;
                }
                CHAR fmtbuf[10], *fmt = fmtbuf;
                /* int potentially overflows, so use unsigned instead.  */
                unsigned p_year = century * 100 + year;
                if (sign)
                    *fmt++ = CQ('-');
                else if (pad == CQ('+') && p_year >= 10000) {
                    *fmt++ = CQ('+');
                    sign = 1;
                }
                if (width && sign)
                    --width;
                *fmt++ = CQ('%');
//                if (pad)
//                    *fmt++ = CQ('0');
                fmt += sprintf(fmt, "0%du", width);
                //STRCPY (fmt, CQ(".*u"));
                len = snprintf(&s[count], maxsize - count, fmtbuf, p_year);
                if (len < 0 || (count += len) >= maxsize)
                    return 0;
            }
                break;
            case CQ('H'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == CQ('O') && *alt_digits)
        {
          len = conv_to_alt_digits (&s[count], maxsize - count,
                    tim_p->tm_hour, *alt_digits);
          CHECK_LENGTH ();
          if (len > 0)
        break;
        }
#endif /* _WANT_C99_TIME_FORMATS */
                /*FALLTHRU*/
            case CQ('k'):    /* newlib extension */
                len = snprintf(&s[count], maxsize - count,
                               *format == CQ('k') ? CQ("%2d") : CQ("%02d"),
                               tim_p->tm_hour);
                CHECK_LENGTH ();
                break;
            case CQ('l'):    /* newlib extension */
                if (alt == CQ('O'))
                    alt = CQ('\0');
                /*FALLTHRU*/
            case CQ('I'): {
                register int h12;
                h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) ?
                      12 : tim_p->tm_hour % 12;
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
        || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                           h12, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count,
                               *format == CQ('I') ? CQ("%02d") : CQ("%2d"), h12);
                CHECK_LENGTH ();
            }
                break;
            case CQ('j'):
                len = snprintf(&s[count], maxsize - count, CQ("%03d"),
                               tim_p->tm_yday + 1);
                CHECK_LENGTH ();
                break;
            case CQ('m'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
          || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                         tim_p->tm_mon + 1, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                               tim_p->tm_mon + 1);
                CHECK_LENGTH ();
                break;
            case CQ('M'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
          || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                         tim_p->tm_min, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                               tim_p->tm_min);
                CHECK_LENGTH ();
                break;
            case CQ('n'):
                if (count < maxsize - 1)
                    s[count++] = CQ('\n');
                else
                    return 0;
                break;
            case CQ('p'):
            case CQ('P'):
                _ctloc (am_pm[tim_p->tm_hour < 12 ? 0 : 1]);
                for (i = 0; i < ctloclen; i++) {
                    if (count < maxsize - 1)
                        s[count++] = (*format == CQ('P') ? TOLOWER (ctloc[i])
                                                         : ctloc[i]);
                    else
                        return 0;
                }
                break;
            case CQ('R'):
                len = snprintf(&s[count], maxsize - count, CQ("%02d:%02d"),
                               tim_p->tm_hour, tim_p->tm_min);
                CHECK_LENGTH ();
                break;
            case CQ('s'):
/*
 * From:
 * The Open Group Base Specifications Issue 7
 * IEEE Std 1003.1, 2013 Edition
 * Copyright (c) 2001-2013 The IEEE and The Open Group
 * XBD Base Definitions
 * 4. General Concepts
 * 4.15 Seconds Since the Epoch
 * A value that approximates the number of seconds that have elapsed since the
 * Epoch. A Coordinated Universal Time name (specified in terms of seconds
 * (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year
 * (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time
 * represented as seconds since the Epoch, according to the expression below.
 * If the year is <1970 or the value is negative, the relationship is undefined.
 * If the year is >=1970 and the value is non-negative, the value is related to a
 * Coordinated Universal Time name according to the C-language expression, where
 * tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:
 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
 *     (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
 *     ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
 * OR
 * ((((tm_year-69)/4 - (tm_year-1)/100 + (tm_year+299)/400 +
 *         (tm_year-70)*365 + tm_yday)*24 + tm_hour)*60 + tm_min)*60 + tm_sec
 */
/* modified from %z case by hoisting offset outside if block and initializing */
            {
                long offset = 0;    /* offset < 0 => W of GMT, > 0 => E of GMT:
				   subtract to get UTC */

                if (tim_p->tm_isdst >= 0) {
                    TZ_LOCK;
                    if (!tzset_called) {
                        _tzset_unlocked ();
                        tzset_called = 1;
                    }

#if defined (__CYGWIN__)
                    /* Cygwin must check if the application has been built with or
           without the extra tm members for backward compatibility, and
           then use either that or the old method fetching from tzinfo.
           Rather than pulling in the version check infrastructure, we
           just call a Cygwin function. */
        extern long __cygwin_gettzoffset (const struct tm *tmp);
        offset = __cygwin_gettzoffset (tim_p);
#elif defined (__TM_GMTOFF)
                    offset = tim_p->__TM_GMTOFF;
#else
                    __tzinfo_type *tz = __gettzinfo();
                    /* The sign of this is exactly opposite the envvar TZ.  We
                       could directly use the global _timezone for tm_isdst==0,
                       but have to use __tzrule for daylight savings.  */
                    offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
#endif
                    TZ_UNLOCK;
                }
                len = snprintf(&s[count], maxsize - count, CQ("%lld"),
                               (((((long long) tim_p->tm_year - 69) / 4
                                  - (tim_p->tm_year - 1) / 100
                                  + (tim_p->tm_year + 299) / 400
                                  + (tim_p->tm_year - 70) * 365 + tim_p->tm_yday) * 24
                                 + tim_p->tm_hour) * 60 + tim_p->tm_min) * 60
                               + tim_p->tm_sec - offset);
                CHECK_LENGTH ();
            }
                break;
            case CQ('S'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
          || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                         tim_p->tm_sec, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                               tim_p->tm_sec);
                CHECK_LENGTH ();
                break;
            case CQ('t'):
                if (count < maxsize - 1)
                    s[count++] = CQ('\t');
                else
                    return 0;
                break;
            case CQ('T'):
                len = snprintf(&s[count], maxsize - count, CQ("%02d:%02d:%02d"),
                               tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec);
                CHECK_LENGTH ();
                break;
            case CQ('u'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == CQ('O') && *alt_digits)
        {
          len = conv_to_alt_digits (&s[count], maxsize - count,
                    tim_p->tm_wday == 0 ? 7
                                : tim_p->tm_wday,
                    *alt_digits);
          CHECK_LENGTH ();
          if (len > 0)
        break;
        }
#endif /* _WANT_C99_TIME_FORMATS */
                if (count < maxsize - 1) {
                    if (tim_p->tm_wday == 0)
                        s[count++] = CQ('7');
                    else
                        s[count++] = CQ('0') + tim_p->tm_wday;
                } else
                    return 0;
                break;
            case CQ('U'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
          || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                         (tim_p->tm_yday + 7 -
                          tim_p->tm_wday) / 7,
                         *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                               (tim_p->tm_yday + 7 -
                                tim_p->tm_wday) / 7);
                CHECK_LENGTH ();
                break;
            case CQ('V'): {
                int adjust = iso_year_adjust(tim_p);
                int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
                int week = (tim_p->tm_yday + 10 - wday) / 7;
                if (adjust > 0)
                    week = 1;
                else if (adjust < 0)
                    /* Previous year has 53 weeks if current year starts on
                       Fri, and also if current year starts on Sat and
                       previous year was leap year.  */
                    week = 52 + (4 >= (wday - tim_p->tm_yday
                                       - isleap (tim_p->tm_year
                                                 + (YEAR_BASE - 1
                                                    - (tim_p->tm_year < 0
                                                       ? 0 : 2000)))));
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
        || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                           week, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"), week);
                CHECK_LENGTH ();
            }
                break;
            case CQ('w'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == CQ('O') && *alt_digits)
        {
          len = conv_to_alt_digits (&s[count], maxsize - count,
                    tim_p->tm_wday, *alt_digits);
          CHECK_LENGTH ();
          if (len > 0)
        break;
        }
#endif /* _WANT_C99_TIME_FORMATS */
                if (count < maxsize - 1)
                    s[count++] = CQ('0') + tim_p->tm_wday;
                else
                    return 0;
                break;
            case CQ('W'): {
                int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
                wday = (tim_p->tm_yday + 7 - wday) / 7;
#ifdef _WANT_C99_TIME_FORMATS
                if (alt != CQ('O') || !*alt_digits
        || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                           wday, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                len = snprintf(&s[count], maxsize - count, CQ("%02d"), wday);
                CHECK_LENGTH ();
            }
                break;
            case CQ('y'): {
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info)
        len = snprintf (&s[count], maxsize - count, CQ("%d"),
                (*era_info)->year);
          else
#endif /* _WANT_C99_TIME_FORMATS */
                {
                    /* Be careful of both overflow and negative years, thanks to
                       the asymmetric range of years.  */
                    int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                                                   : abs(tim_p->tm_year + YEAR_BASE) % 100;
#ifdef _WANT_C99_TIME_FORMATS
                    if (alt != CQ('O') || !*alt_digits
              || !(len = conv_to_alt_digits (&s[count], maxsize - count,
                             year, *alt_digits)))
#endif /* _WANT_C99_TIME_FORMATS */
                    len = snprintf(&s[count], maxsize - count, CQ("%02d"),
                                   year);
                }
                CHECK_LENGTH ();
            }
                break;
            case CQ('Y'):
#ifdef _WANT_C99_TIME_FORMATS
                if (alt == 'E' && *era_info)
        {
          ctloc = (*era_info)->era_Y;
          goto recurse;
        }
      else
#endif /* _WANT_C99_TIME_FORMATS */
            {
                CHAR fmtbuf[10], *fmt = fmtbuf;
                int sign = tim_p->tm_year < -YEAR_BASE;
                /* int potentially overflows, so use unsigned instead.  */
                register unsigned year = (unsigned) tim_p->tm_year
                                         + (unsigned) YEAR_BASE;
                if (sign) {
                    *fmt++ = CQ('-');
                    year = UINT_MAX - year + 1;
                } else if (pad == CQ('+') && year >= 10000) {
                    *fmt++ = CQ('+');
                    sign = 1;
                }
                if (width < 4)
                    width = 4;
                if (width && sign)
                    --width;
                *fmt++ = CQ('%');
//                if (pad)
//                    *fmt++ = CQ('0');
                fmt += sprintf(fmt, "0%du", width);
                //STRCPY (fmt, CQ(".*u"));
                len = snprintf(&s[count], maxsize - count, fmtbuf, year);
                CHECK_LENGTH ();
            }
                break;
            case CQ('z'):
                if (tim_p->tm_isdst >= 0) {
                    long offset;

                    TZ_LOCK;
                    if (!tzset_called) {
                        _tzset_unlocked ();
                        tzset_called = 1;
                    }

#if defined (__CYGWIN__)
                    /* Cygwin must check if the application has been built with or
         without the extra tm members for backward compatibility, and
         then use either that or the old method fetching from tzinfo.
         Rather than pulling in the version check infrastructure, we
         just call a Cygwin function. */
          extern long __cygwin_gettzoffset (const struct tm *tmp);
          offset = __cygwin_gettzoffset (tim_p);
#elif defined (__TM_GMTOFF)
                    offset = tim_p->__TM_GMTOFF;
#else
                    __tzinfo_type *tz = __gettzinfo();
                    /* The sign of this is exactly opposite the envvar TZ.  We
                   could directly use the global _timezone for tm_isdst==0,
                   but have to use __tzrule for daylight savings.  */
                    offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
#endif
                    TZ_UNLOCK;
                    len = snprintf(&s[count], maxsize - count, CQ("%+03ld%02ld"),
                                   offset / SECSPERHOUR,
                                   labs(offset / SECSPERMIN) % 60L);
                    CHECK_LENGTH ();
                }
                break;
            case CQ('Z'):
                if (tim_p->tm_isdst >= 0) {
                    size_t size;
                    const char *tznam = NULL;

                    TZ_LOCK;
                    if (!tzset_called) {
                        _tzset_unlocked ();
                        tzset_called = 1;
                    }
#if defined (__CYGWIN__)
                    /* See above. */
          extern const char *__cygwin_gettzname (const struct tm *tmp);
          tznam = __cygwin_gettzname (tim_p);
#elif defined (__TM_ZONE)
                    tznam = tim_p->__TM_ZONE;
#endif
                    if (!tznam)
                        tznam = _tzname[tim_p->tm_isdst > 0];
                    /* Note that in case of wcsftime this loop only works for
                       timezone abbreviations using the portable codeset (aka ASCII).
                   This seems to be the case, but if that ever changes, this
                   loop needs revisiting. */
                    size = strlen(tznam);
                    for (i = 0; i < size; i++) {
                        if (count < maxsize - 1)
                            s[count++] = tznam[i];
                        else {
                            TZ_UNLOCK;
                            return 0;
                        }
                    }
                    TZ_UNLOCK;
                }
                break;
            case CQ('%'):
                if (count < maxsize - 1)
                    s[count++] = CQ('%');
                else
                    return 0;
                break;
            default:
                return 0;
        }
        if (*format)
            format++;
        else
            break;
    }
    if (maxsize)
        s[count] = CQ('\0');

    return count;
}

size_t lualibc_strfttime(char *s, size_t maxsize, const char *format, const struct tm *tim_p) {
#ifdef _WANT_C99_TIME_FORMATS
    era_info_t *era_info = NULL;
  alt_digits_t *alt_digits = NULL;
  size_t ret = __strftime (s, maxsize, format, tim_p, __get_current_locale (),
               &era_info, &alt_digits);
  if (era_info)
    free_era_info (era_info);
  if (alt_digits)
    free_alt_digits (alt_digits);
  return ret;
#else /* !_WANT_C99_TIME_FORMATS */
    return __strftime (s, maxsize, format, tim_p, NULL,
                       NULL, NULL);
#endif /* !_WANT_C99_TIME_FORMATS */
}
